/**
 * Copyright 2007-2008 非也
 * All rights reserved. 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation。
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses. *
 */
package org.fireflow.engine.impl;

// Generated Feb 23, 2008 12:04:21 AM by Hibernate Tools 3.2.0.b9
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.fireflow.engine.EngineException;
import org.fireflow.engine.IProcessInstance;
import org.fireflow.engine.IRuntimeContextAware;
import org.fireflow.engine.ITaskInstance;
import org.fireflow.engine.IWorkflowSession;
import org.fireflow.engine.IWorkflowSessionAware;
import org.fireflow.engine.RuntimeContext;
import org.fireflow.engine.definition.WorkflowDefinition;
import org.fireflow.engine.event.IProcessInstanceEventListener;
import org.fireflow.engine.event.ProcessInstanceEvent;
import org.fireflow.engine.persistence.IPersistenceService;
import org.fireflow.kernel.IJoinPoint;
import org.fireflow.kernel.INetInstance;
import org.fireflow.kernel.ISynchronizerInstance;
import org.fireflow.kernel.IToken;
import org.fireflow.kernel.KernelException;
import org.fireflow.kernel.impl.JoinPoint;
import org.fireflow.model.EventListener;
import org.fireflow.model.WorkflowProcess;

/**
 * ProcessInstance generated by hbm2java
 */
@SuppressWarnings("serial")
public class ProcessInstance implements IProcessInstance, IRuntimeContextAware, IWorkflowSessionAware, java.io.Serializable {

    private String id = null;
    private String processId = null;
    private Integer version = null;
    private String name = null;
    private String displayName = null;
    private Integer state = null;
    private Boolean suspended = null;
    private String creatorId = null;
    private Date createdTime = null;
    private Date startedTime = null;
    private Date endTime = null;
    private Date expiredTime = null;
    private String parentProcessInstanceId = null;
    private String parentTaskInstanceId = null;

    //null表示尚未初始化
    private Map<String, Object> processInstanceVariables = null;//new HashMap<String, Object>();
    
    
    protected transient RuntimeContext rtCtx = null;
    protected transient IWorkflowSession workflowSession = null;

    public void setRuntimeContext(RuntimeContext ctx) {
        this.rtCtx = ctx;
    }

    public RuntimeContext getRuntimeContext() {
        return this.rtCtx;
    }

    public ProcessInstance() {
        this.state = IProcessInstance.INITIALIZED;
        this.suspended = false;
    }

    public String getId() {
        return this.id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getProcessId() {
        return this.processId;
    }

    public void setProcessId(String processID) {
        this.processId = processID;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDisplayName() {
        return this.displayName;
    }

    public void setDisplayName(String label) {
        this.displayName = label;
    }

    public Integer getState() {
        return this.state;
    }

    public void setState(Integer state) {
        this.state = state;
    }

    public String getParentProcessInstanceId() {
        return parentProcessInstanceId;
    }

    public void setParentProcessInstanceId(String parentProcessInstanceId) {
        this.parentProcessInstanceId = parentProcessInstanceId;
    }

    /**
     * 生成joinPoint 
     * @param synchInst
     * @param token
     * @return
     * @throws EngineException
     */
    public IJoinPoint createJoinPoint(ISynchronizerInstance synchInst, IToken token) throws EngineException {

        int enterTransInstanceCount = synchInst.getEnteringTransitionInstances().size();
        if (enterTransInstanceCount == 0) {//检查流程定义是否合法，同步器节点必须有输入边

            throw new EngineException(this.getId(), this.getWorkflowProcess(),
                    synchInst.getSynchronizer().getId(), "The process definition [" + this.getName() + "] is invalid，the synchronizer[" + synchInst.getSynchronizer() + "] has no entering transition");
        }
        IPersistenceService persistenceService = rtCtx.getPersistenceService();
        //保存到数据库
        persistenceService.saveOrUpdateToken(token);

        IJoinPoint resultJoinPoint = null;
        resultJoinPoint = new JoinPoint();
        resultJoinPoint.setProcessInstance(this);
        resultJoinPoint.setSynchronizerId(synchInst.getSynchronizer().getId());
        if (enterTransInstanceCount == 1) {
            // 生成一个不存储到数据库中的JoinPoint
            resultJoinPoint.addValue(token.getValue());

            if (token.isAlive()) {
                resultJoinPoint.setAlive(true);
                resultJoinPoint.setFromActivityId(token.getFromActivityId());
            }
            resultJoinPoint.setStepNumber(token.getStepNumber() + 1);

            return resultJoinPoint;
        } else {

            int stepNumber = 0;

            List<IToken> tokensList_0 = persistenceService.findTokensForProcessInstance(this.getId(), synchInst.getSynchronizer().getId());
            Map<String,IToken> tokensMap = new HashMap<String,IToken>();
            for (int i = 0; i < tokensList_0.size(); i++) {
                IToken tmpToken =   tokensList_0.get(i);
                String tmpFromActivityId = tmpToken.getFromActivityId();
                if (!tokensMap.containsKey(tmpFromActivityId)) {
                    tokensMap.put(tmpFromActivityId, tmpToken);
                } else {
                	//TODO  ====下面的代码有意义吗？===start===wmj2003
                    IToken tmpToken2 = tokensMap.get(tmpFromActivityId);
                    if (tmpToken2.getStepNumber() > tmpToken.getStepNumber()) {
                        tokensMap.put(tmpFromActivityId, tmpToken2);
                    }
                   //TODO  ====下面的代码有意义吗？===end===wmj2003
                }
            }

            List<IToken> tokensList = new ArrayList<IToken>(tokensMap.values());

            for (int i = 0; i < tokensList.size(); i++) {
                IToken _token = tokensList.get(i);
                resultJoinPoint.addValue(_token.getValue());
                if (_token.isAlive()) {//如果token的状态是alive
                    resultJoinPoint.setAlive(true);
                    String oldFromActivityId = resultJoinPoint.getFromActivityId();
                    if (oldFromActivityId == null || oldFromActivityId.trim().equals("")) {
                        resultJoinPoint.setFromActivityId(_token.getFromActivityId());
                    } else {
                        resultJoinPoint.setFromActivityId(oldFromActivityId + IToken.FROM_ACTIVITY_ID_SEPARATOR + _token.getFromActivityId());
                    }

                }
                if (token.getStepNumber() > stepNumber) {
                    stepNumber = token.getStepNumber();
                }
            }

            resultJoinPoint.setStepNumber(stepNumber + 1);

            return resultJoinPoint;
        }
   
    }

    /* (non-Javadoc)
     * @see org.fireflow.engine.IProcessInstance#run()
     */
    public void run() throws EngineException, KernelException {
        if (this.getState().intValue() != IProcessInstance.INITIALIZED) {
            throw new EngineException(this.getId(),
                    this.getWorkflowProcess(),
                    this.getProcessId(), "The state of the process instance is " + this.getState() + ",can not run it ");
        }

        INetInstance netInstance = rtCtx.getKernelManager().getNetInstance(this.getProcessId(), this.getVersion());
        if (netInstance == null) {
            throw new EngineException(this.getId(),
                    this.getWorkflowProcess(),
                    this.getProcessId(), "The net instance for the  workflow process [Id=" + this.getProcessId() + "] is Not found");
        }
        //触发事件
        ProcessInstanceEvent event = new ProcessInstanceEvent();
        event.setEventType(ProcessInstanceEvent.BEFORE_PROCESS_INSTANCE_RUN);
        event.setSource(this);
        this.fireProcessInstanceEvent(event);

        this.setState(IProcessInstance.RUNNING);
        this.setStartedTime(rtCtx.getCalendarService().getSysDate());
        rtCtx.getPersistenceService().saveOrUpdateProcessInstance(this);
        netInstance.run(this);//运行工作流网实例,从startnode开始
    }

    /* (non-Javadoc)
     * @see org.fireflow.engine.IProcessInstance#getProcessInstanceVariables()
     */
    public Map<String ,Object> getProcessInstanceVariables() {
		IPersistenceService persistenceService = this.rtCtx.getPersistenceService();
    	if (processInstanceVariables==null){
    		//通过数据库查询进行初始化
    		List<ProcessInstanceVar> allVars = persistenceService.findProcessInstanceVariable(this.getId());
    		processInstanceVariables = new HashMap<String ,Object>();
    		if (allVars!=null && allVars.size()!=0){
    			for (ProcessInstanceVar theVar :allVars){
    				processInstanceVariables.put(theVar.getVarPrimaryKey().getName(), theVar.getValue());
    			}
    		}
    	}    	    	
        return processInstanceVariables;
    }

    public void setProcessInstanceVariables(Map<String ,Object> vars) {
        processInstanceVariables = vars;
//        processInstanceVariables.putAll(vars);
    }

    /* (non-Javadoc)
     * @see org.fireflow.engine.IProcessInstance#getProcessInstanceVariable(java.lang.String)
     */
    public Object getProcessInstanceVariable(String name) {
		IPersistenceService persistenceService = this.rtCtx.getPersistenceService();
    	if (processInstanceVariables==null){
    		//通过数据库查询进行初始化
    		List<ProcessInstanceVar> allVars = persistenceService.findProcessInstanceVariable(this.getId());
    		processInstanceVariables = new HashMap<String ,Object>();
    		if (allVars!=null && allVars.size()!=0){
    			for (ProcessInstanceVar theVar :allVars){
    				processInstanceVariables.put(theVar.getVarPrimaryKey().getName(), theVar.getValue());
    			}
    		}
    	}    	
        return processInstanceVariables.get(name);
    }

    /* (non-Javadoc)
     * @see org.fireflow.engine.IProcessInstance#setProcessInstanceVariable(java.lang.String, java.lang.Object)
     */
    public void setProcessInstanceVariable(String name, Object value) {
		IPersistenceService persistenceService = this.rtCtx.getPersistenceService();
    	if (processInstanceVariables==null){
    		//通过数据库查询进行初始化
    		List<ProcessInstanceVar> allVars = persistenceService.findProcessInstanceVariable(this.getId());
    		processInstanceVariables = new HashMap<String ,Object>();
    		if (allVars!=null && allVars.size()!=0){
    			for (ProcessInstanceVar theVar :allVars){
    				processInstanceVariables.put(theVar.getVarPrimaryKey().getName(), theVar.getValue());
    			}
    		}
    	}
    	ProcessInstanceVar procInstVar = new ProcessInstanceVar();
    	ProcessInstanceVarPk pk = new ProcessInstanceVarPk();
    	pk.setProcessInstanceId(this.getId());
    	pk.setName(name);
    	procInstVar.setVarPrimaryKey(pk);
    	procInstVar.setValue(value);
    	procInstVar.setValueType(value.getClass().getName());
    	
    	if (processInstanceVariables.containsKey(name)){
    		persistenceService.updateProcessInstanceVariable(procInstVar);
    	}else{
    		persistenceService.saveProcessInstanceVariable(procInstVar);
    	}
        processInstanceVariables.put(name, value);
    }

    /* (non-Javadoc)
     * @see org.fireflow.engine.IProcessInstance#getWorkflowProcess()
     */
    public WorkflowProcess getWorkflowProcess() throws EngineException {
        WorkflowDefinition workflowDef = rtCtx.getDefinitionService().getWorkflowDefinitionByProcessIdAndVersionNumber(this.getProcessId(), this.getVersion());
        WorkflowProcess workflowProcess = null;

        workflowProcess = workflowDef.getWorkflowProcess();

        return workflowProcess;
    }

    public String getParentTaskInstanceId() {
        return parentTaskInstanceId;
    }

    public void setParentTaskInstanceId(String taskInstId) {
        parentTaskInstanceId = taskInstId;
    }

    public Date getCreatedTime() {
        return this.createdTime;
    }

    public Date getStartedTime() {
        return this.startedTime;
    }

    public Date getEndTime() {
        return this.endTime;
    }

    public void setCreatedTime(Date createdTime) {
        this.createdTime = createdTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    public void setStartedTime(Date startedTime) {
        this.startedTime = startedTime;
    }

    /**
     * 正常结束工作流
     * 1、首先检查有无活动的token,如果有则直接返回，如果没有则结束当前流程
     * 2、执行结束流程的操作，将state的值设置为结束状态
     * 3、然后检查parentTaskInstanceId是否为null，如果不为null则，调用父taskinstance的complete操作。
     */
    public void complete() throws EngineException, KernelException {
        List<IToken> tokens = rtCtx.getPersistenceService().findTokensForProcessInstance(this.getId(), null);
        boolean canBeCompleted = true;
        for (int i = 0; tokens != null && i < tokens.size(); i++) {
            IToken token = tokens.get(i);
            if (token.isAlive()) {
                canBeCompleted = false;
                break;
            }
        }
        if (!canBeCompleted) {
            return;
        }

        this.setState(IProcessInstance.COMPLETED);
        //记录结束时间
        this.setEndTime(rtCtx.getCalendarService().getSysDate());
        rtCtx.getPersistenceService().saveOrUpdateProcessInstance(this);
        
        //删除所有的token
        for (int i = 0; tokens != null && i < tokens.size(); i++) {
            IToken token = tokens.get(i);
            rtCtx.getPersistenceService().deleteToken(token);
        }

        //触发事件
        ProcessInstanceEvent event = new ProcessInstanceEvent();
        event.setEventType(ProcessInstanceEvent.AFTER_PROCESS_INSTANCE_COMPLETE);
        event.setSource(this);
        this.fireProcessInstanceEvent(event);
        if (this.getParentTaskInstanceId() != null && !this.getParentTaskInstanceId().trim().equals("")) {
            ITaskInstance taskInstance = rtCtx.getPersistenceService().findAliveTaskInstanceById(this.getParentTaskInstanceId());
            ((IRuntimeContextAware) taskInstance).setRuntimeContext(rtCtx);
            ((IWorkflowSessionAware) taskInstance).setCurrentWorkflowSession(workflowSession);
            ((TaskInstance) taskInstance).complete(null);
        }
    }

    /* (non-Javadoc)
     * @see org.fireflow.engine.IProcessInstance#abort()
     */
    public void abort() throws EngineException {
        if (this.state.intValue() == IProcessInstance.COMPLETED || this.state.intValue() == IProcessInstance.CANCELED) {
            throw new EngineException(this, this.getWorkflowProcess(), "The process instance can not be aborted,the state of this process instance is " + this.getState());
        }
        IPersistenceService persistenceService = rtCtx.getPersistenceService();
        persistenceService.abortProcessInstance(this);
    }


    /**
     * 触发process instance相关的事件
     * @param e
     * @throws org.fireflow.engine.EngineException
     */
    protected void fireProcessInstanceEvent(ProcessInstanceEvent e) throws EngineException {
        WorkflowProcess workflowProcess = this.getWorkflowProcess();
        if (workflowProcess == null) {
            return;
        }

        List<EventListener> listeners = workflowProcess.getEventListeners();
        for (int i = 0; i < listeners.size(); i++) {
            EventListener listener = listeners.get(i);
            Object obj = rtCtx.getBeanByName(listener.getClassName());
            if (obj != null) {
                ((IProcessInstanceEventListener) obj).onProcessInstanceEventFired(e);
            }
        }
    }

    public Date getExpiredTime() {
        return this.expiredTime;
    }

    public void setExpiredTime(Date arg) {
        this.expiredTime = arg;

    }

    public IWorkflowSession getCurrentWorkflowSession() {
        return this.workflowSession;
    }

    public void setCurrentWorkflowSession(IWorkflowSession session) {
        this.workflowSession = session;
    }

    public String getCreatorId() {
        return this.creatorId;
    }

    public void setCreatorId(String s) {
        this.creatorId = s;
    }

    public Boolean isSuspended() {
        return suspended;
    }
    
    public Boolean getSuspended(){
    	return suspended;
    }

    public void setSuspended(Boolean isSuspended) {
        this.suspended = isSuspended;
    }

    /* (non-Javadoc)
     * @see org.fireflow.engine.IProcessInstance#suspend()
     */
    public void suspend() throws EngineException {
        if (this.state == IProcessInstance.COMPLETED || this.state == IProcessInstance.CANCELED) {
            throw new EngineException(this, this.getWorkflowProcess(), "The process instance can not be suspended,the state of this process instance is " + this.state);
        }
        if (this.isSuspended()) {
            return;
        }
        IPersistenceService persistenceService = this.rtCtx.getPersistenceService();
        persistenceService.suspendProcessInstance(this);
    }

    /* (non-Javadoc)
     * @see org.fireflow.engine.IProcessInstance#restore()
     */
    public void restore() throws EngineException {
        if (this.state == IProcessInstance.COMPLETED || this.state == IProcessInstance.CANCELED) {
            throw new EngineException(this, this.getWorkflowProcess(), "The process instance can not be restored,the state of this process instance is " + this.state);
        }
        if (!this.isSuspended()) {
            return;
        }

        IPersistenceService persistenceService = this.rtCtx.getPersistenceService();
        persistenceService.restoreProcessInstance(this);

    }
}
